1장-함수를 이용한 추상화
1 - 1 프로그래밍의 기본 요소
강력한 프로그래밍 언어는 단순히 컴퓨터가 수행할 과제를 지시하는 수단 이상의 역할을 합니다. 프로그래밍 언어는 우리가 과정에 관한 생각들을 조직화하는 틀로 작용합니다. 따라서 프로그래밍 언어를 고찰할 때는 단순한 아이디어들을 조합해서 더 복잡한 아이디어를 만드는 데 사용하는 수단에 주의를 기울여야 합니다. 모든 강력한 언어는 이를 위해 다음 세 가지 메커니즘을 제공합니다:
- 원시 표현식 (Primitive Expressions): 언어와 관련된 가장 단순한 개체를 나타냅니다.
- 조합 수단 (Means of Combination): 단순한 요소들로부터 복합적인 요소를 만드는 데 사용됩니다.
- 추상화 수단 (Means of Abstraction): 복합적인 요소들에 이름을 붙여서 하나의 단위로 다루는 데 사용됩니다.
프로그래밍에서 우리가 다루는 요소들은 크게 함수와 데이터로 나뉩니다. 데이터는 우리가 조작하고자 하는 재료이고, 함수는 데이터를 다루는 규칙들을 서술한 것입니다. 따라서 모든 강력한 프로그래밍 언어는 반드시 원시 데이터와 원시 함수를 서술하는 기능이 있어야 하고, 그런 함수들과 데이터를 조합하고 추상화하는 수단들도 제공해야 합니다.
이번 장에서는 함수를 구축하는 규칙들에 집중하기 위해 단순한 수치 데이터만 다루기로 합니다. 이후의 장들에서 보겠지만, 이번 장의 규칙들은 복합 데이터를 조작하는 함수를 구축하는 데에도 적용됩니다.
1.1.1 표현식
프로그래밍을 처음 배울 때 유용한 접근 방식 중 하나는 전형적인 상호작용 방식에 따라 자바스크립트 해석기를 직접 사용해 보는 것입니다. 사람과 해석기의 전형적인 상호작용 방식이란, 사람이 해석기의 프롬프트에서 하나의 문장을 입력하고, 해석기가 그 문장을 평가해서 그 결과를 화면에 표시하는 것입니다. 표현식 문장은 표현식과 세미콜론으로 구성됩니다. 표현식은 하나 이상의 원시 표현식으로 구성되는데, 여러 원시 표현식 중 하나로 수가 있습니다. 예를 들어 자바스크립트 해석기에 다음 과 같은 코드를 입력하면 해석기는 다음과 같은 평가 결과를 출력합니다.
486; // 486
수를 나타내는 표현식들을 연산자(+, *)로 조합할 수 있습니다. 그 결과는 연산자들에 해당하는 원시 함수를 해당 수들에 적용하는 하나의 복합 표현식입니다. 다음은 그러한 복합 표현식들의 예입니다.
137 + 349; // 486
1000 - 334; // 666
5 * 99; // 495
10 / 4; // 2.5
2.7 + 10; // 12.7
이처럼 다른 표현식을 구성요소로 담고 있는 표현식을 가리켜 조합이라고 부릅니다. 연산자 조합의 값은 연산자로 지정된 함수를 인수들, 즉 피연산자 값들에 적용해서 구합니다. 연산자를 두 피연산자 사이에 배치하는 관례를 중위 표기법이라고 부릅니다. 중위 표기법은 학교와 일상생활에서 익숙한 수학 표기법과 동일합니다. 수학에서 수식 안에 수식을 중첩하는 것처럼 연산자 조합도 중첩할 수 있습니다. 즉, 연산자 조합 자체를 다른 연산자 조합의 피연산자로 사용할 수 있습니다.
자바스크립트에서도 연산 순서의 혼동을 피하기 위해 소괄호로 연산자 조합들을 묶을 수 있습니다. 소괄호를 생략하면 자바스크립트는 통상적인 관례에 따라 연산 순서를 결정합니다. 예를 들어 다음은 3 _ 5 + 10 / 2는 (3 _ 5) + (10 / 2)에 해당합니다. 이는 연산자 *와 /가 연산자 +와 -보다 우선순위가 높다는 것을 의미합니다.
일련의 덧셈과 뺄셈, 곱셈과 나눗셈도 마찬가지로 오른쪽에서 왼쪽으로 평가됩니다. 예를 들어 1 - 5 / 2 _ 4 + 3는 (1 - ((5 / 2) _ 4)) + 3에 해당합니다. 이를 연산자가 왼쪽 결합이라고 합니다.
표현식들의 중첩과 자바스크립트 해석기가 평가할 수 있는 표현식의 전반적인 복잡도에는 이론적으로 제한이 없습 니다. 예를 들어, 3 _ 2 _ (3 - 5 + 4) + 27 / 6 * 10는 자바스크립트 해석기에 의해 쉽게 57로 평가될 수 있습니다.
복잡한 표현식 문장이 주어져도 해석기는 항상 동일한 기본 주기로 작동합니다. 해석기는 사용자가 입력한 문장을 읽고, 그 문장을 평가하고, 결과를 출력합니다. 이러한 주기를 반복하는 것을 **REPL (read-evaluate-print loop)**이라고 부릅니다. 자바스크립트 REPL에서 출력을 위해 특별한 명령이 필요하지 않음을 주목하십시오. 해석기는 주어진 표현식의 평가 결과를 자동으로 출력합니다.
1.1.2 이름 붙이기와 환경
계산적 객체에 이름을 붙여서 그 이름으로 객체를 지칭하는 수단은 프로그래밍 언어의 필수 기능입니다. 자바스크립트에서 상수에 이름을 붙이는 방법은 const 키워드를 사용하는 상수 선언입니다. 상수 선언을 통해 특정 값을 하나의 이름에 연관시킬 수 있습니다.
예를 들어, 다음 코드에서 해석기는 숫자 2를 size라는 이름에 연관시킵니다. 이제부터 size라는 이름은 값 2를 지칭합니다.
const size = 2;
size; // 2 5 * size; // 10
다음은 const를 사용하여 상수를 선언하고 이를 표현식에 사용하는 예입니다.
const pi = 3.14159; const radius = 10;
const area = pi _ radius _ radius; area; // 314.159
const circumference = 2 _ pi _ radius; circumference; // 62.8318
이처럼 복합적인 연산의 결과를 간단한 이름으로 지칭할 수 있다는 점에서 상수 선언은 가장 단순한 추상화 수단입니다. 예를 들어 circumference는 계산된 원의 둘레를 지칭합니다. 복잡한 구조의 계산적 객체를 사용할 때마다 그 구조를 반복해서 명시하는 것은 불편하므로, 상수 선언을 통해 복잡한 구조의 세부 사항을 기억하고 관리할 수 있습니다.
이름과 값을 연관시키고 이름으로부터 값을 조회하려면 해석기는 이름-객체 쌍을 저장하고 관리하는 특정한 메모리 공간을 필요로 합니다. 이 메모리 공간을 **환경(environment)**이라고 부릅니다. 하나의 계산에 다수의 환경이 관여할 수 있으며, 여기서 말하는 환경은 프로그래밍 환경을 의미합니다.
1.1.3 연산자 조합의 평가
이번 장의 목표 중 하나는 절차적 사고에 관한 논점들을 잘 구분하는 것입니다. 연산자 조합을 평가할 때 해석기가 따르는 절차는 다음과 같습니다:
- 조합의 피연산자 표현식들을 평가한다.
- 연산자가 나타내는 함수를 인수(피연산자들의 값)들에 적용한다.
이 규칙은 계산적인 과정에 관해 일반적으로 중요한 사항 몇 가지를 잘 보여줍니다. 첫째, 단계 1은 주어진 조합의 평가 과정을 완료하기 위해서는 먼저 조합의 각 피연산자를 평가해야 함을 말해줍니다. 이는 규칙의 한 단계에서 규칙 자신을 수행해야 함을 뜻합니다. 즉, 이 평가 규칙은 재귀적입니다.
재귀 덕분에 깊게 중첩된 조합의 평가 규칙도 아주 간결하게 표현할 수 있습니다. 예를 들어, 다음과 같은 복합 연산자 조합을 평가해봅시다.
(2 + 4 _ 6) _ (3 + 12);
이 복합 연산자 조합을 평가하려면 서로 다른 네 가지 조합에 평가 규칙을 적용해야 합니다. 그 과정을 하나의 트리 형태로 시각화할 수 있습니다. 트리의 각 노드는 하나의 조합을 나타내고, 노드에서 뻗어나온 갈래들은 그 조합의 연산자와 피연산자들로 이어집니다. 이러한 트리 표현에서, 조합의 평가 과정은 트리의 말단 노드들에서 출발해서 피연산자 값들을 해당 연산자에 따라 결합해서 점차 위쪽 노드들로 올려보내는 과정에 해당합니다. 일반적으로 재귀는 이처럼 트리 형태의 위계 구조로 조직화된 객체들을 다루는 데 대단히 강력한 기법입니다. 실제로, 이처럼 값들을 위로 올려보내는 형태의 평가 규칙은 트리 누산이라고 부르는 좀 더 일반적인 과정의 한 사례입니다.
단계 1을 재귀적으로 거듭 적용하다 보면 조합이 아니라 원시 표현식 (수치나 이름 같은)을 평가해야 하는 지점에 도달합니다. 원시 표현식 평가에는 다음과 같은 규칙들이 적용됩니다:
- 수치의 값은 해당 숫자들이 나타내는 바로 그 값이다.
- 이름의 값은 현재 환경에서 그 이름에 연관된 객체이다.
여기서 주목할 점은 표현식 안의 이름이 뜻하는 바를 환경이 결정한다는 점입니다. 자바스크립트 같은 대화식(상호작용적) 언어에서 x + 1 같은 표현식의 값을 이야기하려면 x라는 이름의 의미를 제공하는 환경에 관한 정보가 꼭 필요합니다. 평가가 일어나는 문맥을 제공하는 환경이라는 일반적인 개념은 우리가 프로그램의 실행을 이해할 때 중요한 역할을 합니다.